unit uDZIniSerializer;

interface

uses
  SysUtils, Classes, uDZSysUtils,
  uDZStr, uDZSerializer, IniFiles;

{$WARNINGS OFF}

type
  TDzIniClassReader = class(TDzClassReader)
  private
    Ini: TMemIniFile;
    fSectionName: string;
    fHierarchy: TStrings;
    function IniReadString(const ident: string; out value: string): Boolean;
  protected
    procedure StreamChanged; override;
  public
    constructor Create; override;
    destructor Destroy; override;
    function BeginReadRootObject(out ClsName: TNameString): Boolean; override;
    function BeginReadObject(const Name: TNameString): Boolean; override;
    procedure EndReadObject; override;
    function BeginReadList(out Count: Integer): Boolean; override;
    procedure EndReadList; override;
    function BeginReadListItem(Index: Integer): Boolean; override;
    function EndReadListItem: Boolean; override;
    procedure ReadPropList(props: TStrings); override;
    function ReadAnsiChar(const Name: TNameString; out Value: AnsiChar): Boolean; override;
    function ReadWideChar(const Name: TNameString; out Value: WideChar): Boolean; override;
    function ReadDWord(const Name: TNameString; out Value: Integer): Boolean; override;
    function ReadQWord(const Name: TNameString; out Value: Int64): Boolean; override;
    function ReadFloat(const Name: TNameString; out Value: Extended): Boolean; override;

    function ReadAnsiStr(const Name: TNameString;
      out Value: AnsiString): Boolean; override;

    function ReadWideStr(const Name: TNameString;
      out Value: WideString): Boolean; override;

    function ReadUnicodeStr(const Name: TNameString;
      out Value: WideString): Boolean; override;

    function ReadAnsiStrListItem(Index: Integer; out Value: AnsiString;
      out More: Boolean): Boolean; override;

    function ReadWStrListItem(Index: Integer; out Value: WideString;
      out More: Boolean): Boolean; override;

    function ReadUStrListItem(Index: Integer; out Value: WideString;
      out More: Boolean): Boolean; override;

    procedure LoadFromStream(Input: TStream);
  end;

  TDzIniClassWriter = class(TDzClassWriter)
  private
    Ini: TMemIniFile;
    fSectionName: string;
    fHierarchy: TStrings;
  protected
    procedure _WriteWideChar(const Name: TNameString; value: WideChar); override;
    procedure _WriteWord(const Name: TNameString; value: Word); override;
    procedure _WriteDWord(const Name: TNameString; value: Integer); override;
    procedure _WriteQWord(const Name: TNameString; value: Int64); override;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure BeginWriteRootObject(const ClsName: string); override;
    procedure EndWriteRootObject(const Name: TNameString); override;
    function BeginWriteObject(const Name: TNameString): Boolean; override;
    procedure EndWriteObject(const Name: TNameString); override;
    function BeginWriteList(ListKind: TDataType; ItemCount: Integer): Boolean; override;
    procedure EndWriteList; override;
    function BeginWriteListItem(Index: Integer): Boolean; override;
    procedure EndWriteListItem; override;
    procedure WriteAnsiChar(const Name: TNameString; Value: AnsiChar); override;
    procedure WriteByte(const Name: TNameString; Value: Byte); override;
    procedure WriteFloat(const Name: TNameString; Value: Extended); override;
    procedure WriteAnsiStr(const Name: TNameString; const Value: AnsiString); override;
    procedure WriteWideStr(const Name: TNameString; const Value: WideString); override;
    procedure WriteUTF16Str(const Name: TNameString; const Value: WideString); override;
    procedure WriteAnsiStrListItem(Index: Integer; const Value: AnsiString); override;
    procedure WriteWStrListItem(Index: Integer; const Value: WideString); override;
    procedure WriteUStrListItem(Index: Integer; const Value: WideString); override;

    procedure SaveToStream(Output: TStream);
  end;

implementation

{ TDzIniClassReader }

function TDzIniClassReader.BeginReadList(out Count: Integer): Boolean;
begin
  Count := Ini.ReadInteger(fSectionName, 'ItemCount', 0);
  Result := True;
end;

function TDzIniClassReader.BeginReadListItem(Index: Integer): Boolean;
var
  tag, sec: string;
begin
  tag := Format('Item%d', [Index]);
  sec := fSectionName + '.' + tag;
  Result := Ini.SectionExists(sec);
  if Result then
  begin
    fHierarchy.Add(tag);
    fSectionName := sec;
    Result := True;
  end;
end;

function TDzIniClassReader.BeginReadObject(const Name: TNameString): Boolean;
begin
  fHierarchy.Add(Name);
  fSectionName := fSectionName + '.' + Name;
  Result := True;
end;

function TDzIniClassReader.BeginReadRootObject(
  out ClsName: TNameString): Boolean;
begin
  fHierarchy.Clear;
  fHierarchy.Add('Root');
  fSectionName := 'Root';
  Result := True;  
end;

constructor TDzIniClassReader.Create;
begin
  inherited;
  Ini := TMemIniFile.Create('');
  fHierarchy := TStringList.Create;
end;

destructor TDzIniClassReader.Destroy;
begin
  if Ini <> nil then FreeAndNil(Ini);
  fHierarchy.Free;
  inherited;
end;

procedure TDzIniClassReader.EndReadList;
begin

end;

function TDzIniClassReader.EndReadListItem: Boolean;
begin
  SetLength(fSectionName, Length(fSectionName) - 1 -
    Length(fHierarchy[fHierarchy.Count - 1]));
  fHierarchy.Delete(fHierarchy.Count - 1);
  Result := True;
end;

procedure TDzIniClassReader.EndReadObject;
begin
  SetLength(fSectionName, Length(fSectionName) - 1 -
    Length(fHierarchy[fHierarchy.Count - 1]));
  fHierarchy.Delete(fHierarchy.Count - 1);
end;

function TDzIniClassReader.IniReadString(const ident: string;
  out value: string): Boolean;
begin
  Result := Ini.ValueExists(fSectionName, ident);
  if Result then value := Ini.ReadString(fSectionName, ident, '');
end;

procedure TDzIniClassReader.LoadFromStream(Input: TStream);
var
  Strs: TStringList;
  S: AnsiString;
begin
  SetLength(S, Input.Size - Input.Position);
  Input.Read(Pointer(S)^, Length(s));
  Strs := TStringList.Create;
  try
    Strs.Text := S;
    Ini.SetStrings(Strs);
  finally
    Strs.Free;
  end;
end;

function TDzIniClassReader.ReadAnsiChar(const Name: TNameString;
  out value: AnsiChar): Boolean;
var
  str: AnsiString;
begin
  Result := ReadAnsiStr(Name, str) and (str <> '');
  if Result then value := str[1];
end;

function TDzIniClassReader.ReadAnsiStrListItem(Index: Integer;
  out value: AnsiString; out more: Boolean): Boolean;
begin
  Result := IniReadString(Format('Item%d', [Index]), value);
  more := True;
end;

function TDzIniClassReader.ReadAnsiStr(const Name: TNameString;
  out value: AnsiString): Boolean;
begin
  Result := Ini.ValueExists(fSectionName, Name);
  if Result then value := Ini.ReadString(fSectionName, Name, '');
end;

function TDzIniClassReader.ReadFloat(const Name: TNameString;
  out value: Extended): Boolean;
var
  str: string;
begin
  str := Ini.ReadString(fSectionName, Name, '');
  try
    value := StrToFloat(str);
    Result := True;
  except
    Result := False;
  end;
end;

function TDzIniClassReader.ReadQWord(const Name: TNameString;
  out value: Int64): Boolean;
var
  str: string;
  c: Integer;
begin
  str := Ini.ReadString(fSectionName, Name, '');
  Val(str, value, c);
  Result := c = 0;
end;

function TDzIniClassReader.ReadDWord(const Name: TNameString;
  out value: Integer): Boolean;
var
  str: string;
  c: Integer;
begin
  str := Ini.ReadString(fSectionName, Name, '');
  Val(str, value, c);
  Result := c = 0;
end;

procedure TDzIniClassReader.ReadPropList(props: TStrings);
begin

end;

function TDzIniClassReader.ReadUStrListItem(Index: Integer;
  out value: WideString; out more: Boolean): Boolean;
var
  s: string;
begin
  Result := IniReadString(Format('Item%d', [Index]), s);
  if Result then value := WideString(s);
  more := True;
end;

function TDzIniClassReader.ReadUnicodeStr(const Name: TNameString;
  out value: WideString): Boolean;
begin
  Result := Ini.ValueExists(fSectionName, Name);
  if Result then value := Ini.ReadString(fSectionName, Name, '');
end;                                      

function TDzIniClassReader.ReadWideChar(const Name: TNameString;
  out value: WideChar): Boolean;
var
  wstr: WideString;
begin
  Result := ReadWideStr(Name, wstr) and (wstr <> '');
  if Result then value := wstr[1];
end;

function TDzIniClassReader.ReadWStrListItem(Index: Integer;
  out value: WideString; out more: Boolean): Boolean;
var
  s: string;
begin
  Result := IniReadString(Format('Item%d', [Index]), s);
  if Result then value := WideString(s);
  more := True;
end;

procedure TDzIniClassReader.StreamChanged;
begin
  inherited;
  LoadFromStream(Stream);
end;

function TDzIniClassReader.ReadWideStr(const Name: TNameString;
  out value: WideString): Boolean;
begin
  Result := Ini.ValueExists(fSectionName, Name);
  if Result then value := Ini.ReadString(fSectionName, Name, '');
end;

{ TDzIniClassWriter }

function TDzIniClassWriter.BeginWriteList(ListKind: TDataType;
  ItemCount: Integer): Boolean;
begin
  Ini.WriteInteger(fSectionName, 'ItemCount', ItemCount);
  Result := True;
end;

function TDzIniClassWriter.BeginWriteListItem(Index: Integer): Boolean;
var
  tag: AnsiString;  
begin
  tag := Format('Item%d', [Index]);
  fHierarchy.Add(tag);
  fSectionName := Format('%s.%s', [fSectionName, tag]);
  Result := True;
end;

function TDzIniClassWriter.BeginWriteObject(const Name: TNameString): Boolean;
begin
  fHierarchy.Add(Name);
  fSectionName := fSectionName + '.' + Name;
  Result := True;
end;

procedure TDzIniClassWriter.BeginWriteRootObject(const ClsName: string);
begin
  Ini.Clear;
  fHierarchy.Clear;
  fHierarchy.Add('Root');
  fSectionName := 'Root';
end;

constructor TDzIniClassWriter.Create();
begin
  inherited;
  fHierarchy := TStringList.Create;
  Ini := TMemIniFile.Create('');
end;

destructor TDzIniClassWriter.Destroy;
begin
  Ini.Free;
  fHierarchy.Free;
  inherited;
end;

procedure TDzIniClassWriter.EndWriteList;
begin

end;

procedure TDzIniClassWriter.EndWriteListItem;
begin
  SetLength(fSectionName, Length(fSectionName) - 1 -
    Length(fHierarchy[fHierarchy.Count - 1]));
  fHierarchy.Delete(fHierarchy.Count - 1);
end;

procedure TDzIniClassWriter.EndWriteObject;
begin
  SetLength(fSectionName, Length(fSectionName) - 1 -
    Length(fHierarchy[fHierarchy.Count - 1]));
  fHierarchy.Delete(fHierarchy.Count - 1);  
end;

procedure TDzIniClassWriter.EndWriteRootObject(const Name: TNameString);
begin
  EndWriteObject(Name);
  if Assigned(Stream) then SaveToStream(Stream);
end;

procedure TDzIniClassWriter.SaveToStream(Output: TStream);
var
  List: TStringList;
  S: string;
begin
  List := TStringList.Create;
  try
    Ini.GetStrings(List);
    S := List.Text;
    Output.Write(Pointer(S)^, Length(S) * SizeOf(Char));
  finally
    List.Free;
  end;
end;

procedure TDzIniClassWriter.WriteAnsiChar(const Name: TNameString;
  value: AnsiChar);
begin
  Ini.WriteString(fSectionName, Name, value);
end;

procedure TDzIniClassWriter.WriteAnsiStrListItem(Index: Integer;
  const value: AnsiString);
begin
  Ini.WriteString(fSectionName, Format('Item%d', [Index]), Value);
end;

procedure TDzIniClassWriter.WriteByte(const Name: TNameString; value: Byte);
begin
  Ini.WriteInteger(fSectionName, Name, value);
end;

procedure TDzIniClassWriter.WriteAnsiStr(const Name: TNameString;
  const value: AnsiString);
begin
  Ini.WriteString(fSectionName, Name, value);
end;

procedure TDzIniClassWriter.WriteFloat(const Name: TNameString; value: Extended);
begin
  Ini.WriteFloat(fSectionName, Name, value);
end;

procedure TDzIniClassWriter._WriteQWord(const Name: TNameString; value: Int64);
begin
  Ini.WriteString(fSectionName, Name, IntToStr(value));
end;

procedure TDzIniClassWriter._WriteDWord(const Name: TNameString; value: Integer);
begin
  Ini.WriteInteger(fSectionName, Name, value);
end;

procedure TDzIniClassWriter.WriteUStrListItem(Index: Integer;
  const value: WideString);
begin
  Ini.WriteString(fSectionName, Format('Item%d', [Index]), Value);
end;

procedure TDzIniClassWriter.WriteUTF16Str(const Name: TNameString;
  const value: WideString);
begin
  Ini.WriteString(fSectionName, Name, value);
end;

procedure TDzIniClassWriter._WriteWideChar(const Name: TNameString;
  value: WideChar);
begin
  Ini.WriteString(fSectionName, Name, value);
end;

procedure TDzIniClassWriter.WriteWStrListItem(Index: Integer;
  const value: WideString);
begin
  Ini.WriteString(fSectionName, Format('Item%d', [Index]), Value);
end;

procedure TDzIniClassWriter.WriteWideStr(const Name: TNameString;
  const value: WideString);
begin
  Ini.WriteString(fSectionName, Name, value);
end;

procedure TDzIniClassWriter._WriteWord(const Name: TNameString; value: Word);
begin
  Ini.WriteInteger(fSectionName, Name, value);
end;

end.
